#pragma once

#include <LogStream.h>
#include <functional>

#define LOG_TRACE if(AsyncLogger::Logger::logLevel() <= AsyncLogger::LogLevel::TRACE) \
	AsyncLogger::Logger(__FILE__,__LINE__,AsyncLogger::LogLevel::TRACE,__func__).stream()

#define LOG_DEBUG if(AsyncLogger::Logger::logLevel() <= AsyncLogger::LogLevel::DEBUG) \
	AsyncLogger::Logger(__FILE__,__LINE__,AsyncLogger::LogLevel::DEBUG,__func__).stream()

#define LOG_INFO if(AsyncLogger::Logger::logLevel() <= AsyncLogger::LogLevel::INFO) \
	AsyncLogger::Logger(__FILE__,__LINE__,AsyncLogger::LogLevel::INFO,__func__).stream()

#define LOG_WARN AsyncLogger::Logger(__FILE__,__LINE__,AsyncLogger::LogLevel::WARN,__func__).stream()

#define LOG_ERROR AsyncLogger::Logger(__FILE__,__LINE__,AsyncLogger::LogLevel::ERROR_).stream()

#define LOG_FATAL AsyncLogger::Logger(__FILE__,__LINE__,AsyncLogger::LogLevel::FATAL).stream()

#define LOG_NUM_LOG_LEVELS AsyncLogger::Logger(__FILE__,__LINE__,AsyncLogger::LogLevel::NUM_LOG_LEVELS).stream()

namespace AsyncLogger {
	enum LogLevel
	{
		TRACE,
		DEBUG,
		INFO,
		WARN,
		ERROR_,
		FATAL,
		NUM_LOG_LEVELS,
	};

	class Logger {
		using OutputFunc = std::function<void(const char*, int)>;
	public:
		Logger();

		Logger(const std::string& file, int line, LogLevel level, const char* func);

		Logger(const std::string& file, int line, LogLevel level);

		static void setOutput(OutputFunc func);

		static void setLogLevel(LogLevel level);

		LogStream& stream() {
			return impl_.stream();
		};

		static OutputFunc outputFunc() {
			return func_;
		}

		static LogLevel logLevel() {
			return level_;
		}

		~Logger();

	private:
		class Impl {
		public:
			Impl();

			Impl(const std::string& file, int line, LogLevel level, const char* func);

			Impl(const std::string& file, int line, LogLevel level);

			LogStream& stream();

			const char* data();

			size_t len();
		
		private:
			LogStream stream_;
		};

	private:
		static OutputFunc func_;
		static LogLevel level_;
		Impl impl_;
	};
}